package com.demo.gulimall.order.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.demo.gulimall.common.constant.OrderConstant;
import com.demo.gulimall.common.exception.NoStockException;
import com.demo.gulimall.common.to.SkuHasStockVo;
import com.demo.gulimall.common.to.mq.SeckillOrderTo;
import com.demo.gulimall.common.utils.R;
import com.demo.gulimall.common.constant.OrderStatusEnum;
import com.demo.gulimall.order.entity.*;
import com.demo.gulimall.order.feign.*;
import com.demo.gulimall.order.mapper.OrderMapper;
import com.demo.gulimall.order.service.*;
import com.demo.gulimall.order.to.OrderCreatedTo;
import com.demo.gulimall.order.vo.*;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * 订单
 *
 * @author Chen Yuan
 */

@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {

    private ThreadLocal<OrderSubmitVo> submitVoThreadLocal = new ThreadLocal<>();

    @Autowired
    private OrderItemService orderItemService;

    @Autowired
    private OrderOperateHistoryService orderOperateHistoryService;

    @Autowired
    private MemberFeignService memberFeignService;

    @Autowired
    private CartFeignService cartFeignService;

    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;

    @Autowired
    private WareFeignService wareFeignService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private ProductFeignService productFeignService;

    @Autowired
    private PaymentInfoService paymentInfoService;

    // 查询订单确认页面所需数据
    @Override
    public OrderConfirmVo confirmOrder(Long userId) throws ExecutionException, InterruptedException {
        OrderConfirmVo orderConfirmVo = new OrderConfirmVo();

        // ### 解决异步调用时 Feign 上下文共享问题, 将当前线程的上下文手动传入副线程
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

        // 查询当前会员的所有收货地址列表
        CompletableFuture<Void> addressFuture = CompletableFuture.runAsync(() -> {
            // ### 解决异步调用时 Feign 上下文共享问题
            RequestContextHolder.setRequestAttributes(requestAttributes);
            R<List<MemberAddressVo>> addressRes = memberFeignService.getAddress(userId);
            List<MemberAddressVo> addresses = addressRes.getData();
            orderConfirmVo.setAddresses(addresses);
        }, threadPoolExecutor);

        // 查询购物车所有选中的购物项
        CompletableFuture<Void> orderItemFuture = CompletableFuture.runAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            R<List<OrderItemVo>> cartItemsRes = cartFeignService.getCheckedCartItemsByUserId(userId);
            List<OrderItemVo> cartItems = cartItemsRes.getData();
            orderConfirmVo.setOrderItems(cartItems);
        }, threadPoolExecutor).thenRunAsync(()->{
            // 查询库存
            List<OrderItemVo> cartItems = orderConfirmVo.getOrderItems();
            // 获取所有商品ID
            List<Long> skuIds = cartItems.stream().map(OrderItemVo::getSkuId).collect(Collectors.toList());
            // 查询所有商品库存
            R<List<SkuHasStockVo>> stockRes = wareFeignService.getSkuHasStock(skuIds);
            List<SkuHasStockVo> stockVo = stockRes.getData();
            Map<Long, Boolean> stockMap = stockVo.stream().collect(Collectors.toMap(SkuHasStockVo::getSkuId, SkuHasStockVo::getHasStock));
            // 将库存塞入
            cartItems = cartItems.stream().peek(item -> item.setHasStock(stockMap.get(item.getSkuId()))).collect(Collectors.toList());
            orderConfirmVo.setOrderItems(cartItems);
        }, threadPoolExecutor);

        // 查询用户积分
        orderConfirmVo.setIntegration(1);

        //  防止重复提交: 生成唯一ID, 存入 Redis 并返回给前端, 提交订单时判断改ID
        String token = UUID.randomUUID().toString().replace("-", "");
        orderConfirmVo.setOrderToken(token);
        redisTemplate.opsForValue().set(OrderConstant.USER_ORDER_TOKEN_PREFIX + userId, token, 30, TimeUnit.MINUTES);

        CompletableFuture.allOf(addressFuture, orderItemFuture).get();
        return orderConfirmVo;
    }

    @Override
    @Transactional
    public OrderSubmitResVo submitOrder(OrderSubmitVo orderSubmitVo) {

    // 1. 验证令牌, 传入的与Redis中存的令牌信息都不为空, 且一样才通过
        //String tokenInRedis = redisTemplate.opsForValue().get(OrderConstant.USER_ORDER_TOKEN_PREFIX + orderSubmitVo.getUserId());
        //if (StringUtils.isNotEmpty(tokenInRedis) &&
        //        !Objects.equals(tokenInRedis, orderSubmitVo.getOrderToken())) {
        //    // 令牌验证不通过, 直接返回
        //    return new OrderSubmitResVo(null, 1, "请勿重复提交");
        //}
        //// 令牌验证通过直接删除
        //redisTemplate.delete(OrderConstant.USER_ORDER_TOKEN_PREFIX + orderSubmitVo.getUserId());

        // 上面验证令牌的部分要保证原子性, 可以使用 LUA 脚本
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        // 脚本执行返回值 —— 0失败, 1成功 ; 参数: 脚本对象, RedisKey的数组, 需要对比的Value的数组
        Long tokenRes = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Arrays.asList(OrderConstant.USER_ORDER_TOKEN_PREFIX + orderSubmitVo.getUserId()), orderSubmitVo.getOrderToken());
        System.out.println(tokenRes);
        if (null == tokenRes || tokenRes == 0) {
            // 令牌验证失败, 直接返回
            //return new OrderSubmitResVo(null, 1, "请勿重复提交");
        }
        // 令牌验证成功, 执行后续操作

        // 使用 ThreadLocal 共享数据, 改对象为多个方法都需要的参数
        submitVoThreadLocal.set(orderSubmitVo);

    // 2. 创建订单
        OrderCreatedTo orderCreatedTo = this.createOrder();

    // 3. 验价, 前端传入的 和 后端计算的价格相差 0.01 以内就算成功
        BigDecimal subtract = orderCreatedTo.getOrder().getPayAmount().subtract(orderSubmitVo.getTotal()).abs();

        if (subtract.compareTo(new BigDecimal("0.01")) > 0) {
            // 价差 > 0.01, 验价不通过
            return new OrderSubmitResVo(null, 2, ("后端计算的价格:[" + orderCreatedTo.getOrder().getPayAmount() +"] 与 前端计算价格:[" + orderSubmitVo.getTotal().toPlainString() + "] 相差超过 0.01 !"));
        }

    // 4. 验价通过, 保存订单
        this.saveOrderInfo(orderCreatedTo);

    // 5. 锁定库存
        SkuLockStockVo skuLockStockVo = new SkuLockStockVo();
        List<OrderItemVo> itemVos = orderCreatedTo.getOrderItems().stream().map(item -> {
            OrderItemVo itemVo = new OrderItemVo();
            itemVo.setSkuId(item.getSkuId())
                    .setCount(item.getSkuQuantity())
                    .setTitle(item.getSpuName());
            return itemVo;
        }).collect(Collectors.toList());
        skuLockStockVo.setOrderSn(orderCreatedTo.getOrder().getOrderSn())
                        .setOrderItems(itemVos);
        R lockStockResRes = wareFeignService.orderLockStock(skuLockStockVo);
        if (!lockStockResRes.isSuccess()) {
            // 锁定失败, 回滚
            //return new OrderSubmitResVo(null, 3, lockStockResRes.getMsg());
            throw new NoStockException(lockStockResRes.getMsg());
        }

    // 6. 远程计算积分
    //    int error = 10 / 0; //(模拟发生异常)

    // 7. 订单创建成功, 发送消息
        rabbitTemplate.convertAndSend("order-event-exchange", "order.create.order", orderCreatedTo.getOrder());

        return new OrderSubmitResVo(orderCreatedTo.getOrder(), 0, "提交成功");
    }

    @Override
    public Order getOrderBySn(String orderSn) {
        return this.getOne(new QueryWrapper<Order>().eq("order_sn", orderSn));
    }

    @Override
    public Boolean closeOrder(Order param) {
        // 1. 查询当前订单的最新状态
        Order order = this.getById(param.getId());
        if (!order.getStatus().equals(OrderStatusEnum.CREATE_NEW.getCode())){
            return true;
        }

        // 只有新建状态需要关单
        Order updateOd = new Order();
        updateOd.setId(order.getId())
                .setStatus(OrderStatusEnum.CANCLED.getCode());
        this.updateById(updateOd);

        // 订单关闭成功, 向库存发送消息, 通知解锁库存
        try {
            rabbitTemplate.convertAndSend("order-event-exchange", "order.create.other", order);
        } catch (Exception e) {
            // 如果消息发送失败, 则做好日志记录
        }
        return true;
    }

    /**
     * 获取当前订单支付信息
     * @param orderSn
     * @return
     */
    @Override
    public PayVo getOrderPay(String orderSn) {
        Order order = this.getOrderBySn(orderSn);

        List<OrderItem> orderItems = orderItemService.list(new QueryWrapper<OrderItem>().eq("order_sn", orderSn));

        String amount = order.getTotalAmount().setScale(2, BigDecimal.ROUND_UP).toString();

        PayVo payVo = new PayVo();
        payVo.setBody(orderItems.get(0).getSkuAttrsVals())
                .setOut_trade_no(orderSn)
                .setSubject(orderItems.get(0).getSkuName())
                .setTotal_amount(amount);
        return payVo;
    }

    @Override
    public String handlePayResult(PayAsyncVo asyncVo) {
        // 保存交易流水
        PaymentInfo paymentInfo = new PaymentInfo();
        paymentInfo.setAlipayTradeNo(asyncVo.getTrade_no())
                .setOrderSn(asyncVo.getOut_trade_no())
                .setPaymentStatus(asyncVo.getTrade_status())
                .setCallbackTime(asyncVo.getNotify_time());
        paymentInfoService.save(paymentInfo);

        // 修改订单状态
        if (Objects.equals(asyncVo.getTrade_status(), "TRADE_SUCCESS") ||
                Objects.equals(asyncVo.getTrade_status(), "TRADE_FINISHED")) {
            // 支付成功
            baseMapper.updateOrderStatusBySn(asyncVo.getOut_trade_no(), OrderStatusEnum.PAYED.getCode());
        }


        return null;
    }

    @Override
    public void createSeckillOrder(SeckillOrderTo orderTo) {
        // 保存订单信息
        Order order = new Order();
        order.setOrderSn(orderTo.getOrderSn())
                .setMemberId(orderTo.getMemberId())
                .setStatus(OrderStatusEnum.CREATE_NEW.getCode())
                .setPayAmount(orderTo.getSeckillPrice().multiply(new BigDecimal(orderTo.getSeckillNum())));
        this.save(order);


        // 保存订单项信息
        OrderItem orderItem = new OrderItem();
        orderItem.setOrderSn(orderTo.getOrderSn())
                .setRealAmount(order.getPayAmount())
                .setSkuQuantity(orderTo.getSeckillNum());

        //TODO: 2023/8/8 设置其他商品信息 productFeignService.getSpuInfoBySkuId(orderTo.getSkuId())
        orderItemService.save(orderItem);
    }


    //  保存订单数据
    private void saveOrderInfo(OrderCreatedTo createdOrder) {
        // 保存订单
        this.save(createdOrder.getOrder());

        // 保存订单项目
        orderItemService.saveBatch(createdOrder.getOrderItems());

        // 保存订单操作记录
        OrderOperateHistory operateHistory = new OrderOperateHistory();
        operateHistory.setOrderId(createdOrder.getOrder().getOrderSn())
                        .setOperateMan(createdOrder.getOrder().getMemberId().toString())
                        .setOrderStatus(0)
                        .setNote("创建订单");
        orderOperateHistoryService.save(operateHistory);
    }

    // 创建待保存的订单对象
    private OrderCreatedTo createOrder(){
        OrderCreatedTo orderCreatedTo = new OrderCreatedTo();
        // 生成订单ID
        String orderId = IdWorker.getIdStr();
        // 准备 Order 数据
        Order order = buildOrder(orderId);

        // 准备 OrderItem 数据
        List<OrderItem> orderItems = this.buildOrderItemList(orderId);

        // 计算价格相关
        this.computePrice(order, orderItems);

        orderCreatedTo.setOrder(order)
                .setOrderItems(orderItems);
        return orderCreatedTo;
    }

    // 计算订单价格
    private void computePrice(Order order, List<OrderItem> orderItems) {
        // 计算所有商品项目的费用总和
        BigDecimal totalPrice = BigDecimal.ZERO, promotionAmount = BigDecimal.ZERO, couponAmount = BigDecimal.ZERO, integrationAmount = BigDecimal.ZERO;
        int giftIntegration = 0, giftGrowth = 0;
        for (OrderItem orderItem : orderItems) {
            totalPrice = totalPrice.add(orderItem.getRealAmount());
            promotionAmount = promotionAmount.add(orderItem.getPromotionAmount());
            couponAmount = couponAmount.add(orderItem.getCouponAmount());
            integrationAmount = integrationAmount.add(orderItem.getIntegrationAmount());

            giftIntegration = giftIntegration + orderItem.getGiftIntegration();
            giftGrowth = giftGrowth + orderItem.getGiftGrowth();
        }

        order.setTotalAmount(totalPrice) // 费用
                .setPayAmount(totalPrice.add(order.getFreightAmount()))
                .setPromotionAmount(promotionAmount)
                .setCouponAmount(couponAmount)
                .setIntegrationAmount(integrationAmount)
                .setIntegration(giftIntegration)
                .setGrowth(giftGrowth)
                .setDeleteStatus(0);
    }

    // 构建订单
    private Order buildOrder(String orderId) {
        // 获取订单提交对象
        OrderSubmitVo orderSubmitVo = submitVoThreadLocal.get();

        R<FareVo> fareRes = wareFeignService.getFare(orderSubmitVo.getAddressId());
        FareVo fareVo = fareRes.getData();

        Order order = new Order();
        order.setOrderSn(orderId)
                //.setFreightAmount(fareVo.getFare())
                .setFreightAmount(BigDecimal.ZERO) // 先不加运费
                .setReceiverProvince(fareVo.getAddress().getProvince())
                .setReceiverRegion(fareVo.getAddress().getRegion())
                .setReceiverCity(fareVo.getAddress().getCity())
                .setReceiverDetailAddress(fareVo.getAddress().getDetailAddress())
                .setReceiverName(fareVo.getAddress().getName())
                .setReceiverPhone(fareVo.getAddress().getPhone())
                .setReceiverPostCode(fareVo.getAddress().getPostCode())
                .setStatus(OrderStatusEnum.CREATE_NEW.getCode())
                .setAutoConfirmDay(7)
                .setMemberId(orderSubmitVo.getUserId());

        return order;
    }

    // 构建订单项目列表
    private List<OrderItem> buildOrderItemList(String orderId) {
        OrderSubmitVo orderSubmitVo = submitVoThreadLocal.get();
        R<List<OrderItemVo>> cartItemRes = cartFeignService.getCheckedCartItemsByUserId(orderSubmitVo.getUserId());
        List<OrderItemVo> cartItems = cartItemRes.getData();
        List<OrderItem> orderItems = null;
        if (null != cartItems && cartItems.size() > 0) {
            orderItems = cartItems.stream().map(item -> {
                OrderItem orderItem = buildOrderItem(item);
                orderItem.setOrderSn(orderId);
                return orderItem;
            }).collect(Collectors.toList());
        }

        return orderItems;
    }

    // 构建订单项目对象
    private OrderItem buildOrderItem(OrderItemVo item) {
        OrderItem orderItem = new OrderItem();
        // 1. 订单信息
        // 2. SPU 信息
        R<SpuInfoVo> spuInfoRes = productFeignService.getSpuInfoBySkuId(item.getSkuId());
        SpuInfoVo spuInfo = spuInfoRes.getData();
        // 3. SKU 信息
        // 4. 商品优惠信息
        // 计算商品价格: (单价 * 数量) - 各种优惠jine
        BigDecimal promotionAmount = BigDecimal.ZERO;
        BigDecimal couponAmount = BigDecimal.ZERO;
        BigDecimal integrationAmount = BigDecimal.ZERO;
        BigDecimal realAmount = item.getTotalPrice().subtract(promotionAmount).subtract(couponAmount).subtract(integrationAmount);
        // 5. 积分信息

        orderItem.setSpuId(spuInfo.getId())// spuInfo
                .setSpuName(spuInfo.getSpuName())
                .setSpuPic(null)
                .setSpuBrand(spuInfo.getBrandId().toString())
                .setCategoryId(spuInfo.getCatalogId())
                .setSkuId(item.getSkuId()) // skuInfo
                .setSkuName(item.getTitle())
                .setSkuPic(item.getImage())
                .setSkuPrice(item.getPrice())
                .setSkuQuantity(item.getCount())
                .setSkuAttrsVals(StringUtils.collectionToDelimitedString(item.getSkuAttrs(), ";"))
                .setPromotionAmount(promotionAmount) // 商品优惠信息
                .setCouponAmount(couponAmount)
                .setIntegrationAmount(integrationAmount)
                .setRealAmount(realAmount)
                .setGiftIntegration(item.getTotalPrice().intValue()) // 积分信息
                .setGiftGrowth(item.getTotalPrice().intValue());

        return orderItem;
    }
}

